Spread.Viewsのさまざまな拡張機能を使用すると、包括的なEコマースWebサイトのデザインレイアウトを作成できます。Spread.Viewsの最適化されたユーザーインタフェースは、大画面デバイスはもちろん、モバイル画面やタブレット画面でもサポートされます。
ここで紹介するデモは、Spread.Viewsの以下の機能を使用することで作成できます。
Spread.Viewsを使用して、次のイメージのようなEコマースWebサイトのインタフェースを作成するには、次の手順を実行します。
サンプルコード
- 以下のCSSおよびJS参照をプロジェクトに追加します。
<link rel="stylesheet" type="text/css" href="[Your Stylesheet Path]/gc.spread.views.dataview.10.0.0.css"> <link rel="stylesheet" type="text/css" href="[Your Stylesheet Path]/bootstrap-snippet.min.css"> <link rel="stylesheet" type="text/css" href="[Your Stylesheet Path]/font-awesome.min.css"> <link rel="stylesheet" type="text/css" href="[Your Stylesheet Path]/gc.spread.views.cardlayout.10.0.0.css" /> <script src="[Your Script Path]/gc.spread.common.10.0.0.min.js" type="text/javascript"></script> <script src="[Your Script Path]/gc.spread.views.dataview.10.0.0.min.js" type="text/javascript"></script> <script src="[Your Script Path]/lodash.min.js" type="text/javascript"></script> <script src="[Your Script Path]/gc.spread.views.cardlayout.10.0.0.min.js" type="text/javascript"></script> <script src="[Your Script Path]/zepto.min.js" type="text/javascript"></script> <script src="[Your Script Path]/license.js" type="text/javascript"></script> <script src="data/TVData.js" type="text/javascript"></script>
headタグ内に、インタフェースにスタイルを適用するためのstyleタグを追加します。
* { -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } ::-webkit-scrollbar { width: 5px; height: 5px; } ::-webkit-scrollbar-thumb { background: #e0e0e0; border-radius: 5px; } label { font-weight: normal; margin: 0; } .card-layout.gc-grid { border: none; } .card-layout .gc-row { text-align: center; padding: 3px; } #grid1 { position: absolute; left: 220px; right: 0; height: 100%; } .demo-container { -moz-user-select: none; -khtml-user-select: none; -webkit-user-select: none; -ms-user-select: none; user-select: none; width: 100%; height: 100%; position: relative; } .filter-panel { float: left; width: 219px; height: 100%; overflow: auto; display: block; border: none; z-index: auto; position: static; } .filter-header { color: #999; font-size: 17px; padding-bottom: 10px; padding-top: 6px; } .filter-details { font-size: 14px; font-weight: 700; padding-bottom: 3px; } .stars-box { display: inline-block; height: 13px; width: 65px; overflow: hidden; background-image: url("./images/star-ratings.png"); vertical-align: middle; } .stars-0 { background-position: -65px 0; } .stars-0-5 { background-position: -52px -19px; } .stars-1 { background-position: -52px 0; } .stars-1-5 { background-position: -39px -19px; } .stars-2 { background-position: -39px 0; } .stars-2-5 { background-position: -26px -19px; } .stars-3 { background-position: -26px 0; } .stars-3-5 { background-position: -13px -19px; } .stars-4 { background-position: -13px 0; } .stars-4-5 { background-position: 0 -18px; } .stars-5 { background-position: 0 0; } .tv-image { margin-left: 10px; } .tv-brand { font-size: 13px; color: lightgrey; margin-bottom: 8px; } .tv-price { color: #b12704; } .mobile-filter-entry { display: none; } .filter-condition { display: inline-block; border: 1px solid lightgrey; width: 100px; height: 25px; font-family: -apple-system-font, "Helvetica Neue", 'Segoe UI', Helvetica, Arial, sans-serif; font-size: 16px; text-align: center; cursor: pointer; } .slicer { margin: 10px 0; } .slicer-item { margin: 2px 0; cursor: pointer; } .hover { color: #E47911; } .filtered { background-color: #FFFFFF; } .filteredOutByOther { color: #A6A8B1; } .filteredOutBySelf { background-color: #FFFFFF; } @media only screen and (max-width: 768px) { #grid1 { position: static; height: 90%; } .filter-panel { margin-top: -1px; border: 1px solid lightgrey; width: 100%; overflow: auto; height: 90%; position: absolute; z-index: 1; background: white; padding: 5px 0 0 10px; display: none; } .mobile-filter-entry { height: 25px; display: block; } }
bodyタグ内にdivタグを追加して、ページ内のコンテナとしてDOM要素を含めます。
<div class="demo-container"> <div class="mobile-filter-entry"> <div class="filter-condition"><span style="padding-right:5px;">Filter</span><span class="fa fa-angle-down"></span></div> </div> <div class="filter-panel"> <div class="filter-header">Refine by</div> <div id="tv_display_size" class="slicer"></div> <div id="tv_resolution" class="slicer"></div> <div id="customer_review_star" class="slicer"></div> </div> <div id="grid1"></div> </div> <script type="text/javascript"> var slicerComponentNS = GC.Spread.Slicers; var rowTemplate = '<div>' + '<div data-column="image"></div>' + '<div data-column="description"></div>' + '<div data-column="brand"></div>' + '<div data-column="price"></div>' + '<div data-column="starsIcon"></div>' + '</div>'; var imagePresenter = '<img class="tv-image" src={{=it.image}} />'; var descriptionPresenter = '<a href="#"><b>{{=it.description}}</b></a>'; var brandPresenter = '<div class="tv-brand"><label>by {{=it.brand}}</label></div>'; var pricePresenter = '<div>${{=it.price}}</div>'; var startPresenter = '<div class="stars-box {{=it.starsIcon}}"></div>';
Spread.Viewsのインスタンスを作成するための変数定義を追加します。
var dataView; var rowTemplate = '<div>' + '<div data-column="image"></div>' + '<div data-column="description"></div>' + '<div data-column="brand"></div>' + '<div data-column="price"></div>' + '<div data-column="starsIcon"></div>' + '</div>'; var imagePresenter = '<img class="tv-image" src={{=it.image}} />'; var descriptionPresenter = '<a href="#"><b>{{=it.description}}</b></a>'; var brandPresenter = '<div class="tv-brand"><label>by {{=it.brand}}</label></div>'; var pricePresenter = '<div>${{=it.price}}</div>'; var startPresenter = '<div class="stars-box {{=it.starsIcon}}"></div>';
- グリッド値を指定する列定義を追加します。
var columns = [{ id: 'image', caption: 'Image', dataField: 'image', presenter: imagePresenter }, { id: 'description', caption: 'Description', dataField: 'description', presenter: descriptionPresenter }, { id: 'brand', caption: 'Brand', dataField: 'brand', presenter: brandPresenter }, { id: 'price', caption: 'Price', dataField: 'price', presenter: pricePresenter }, { id: 'starsIcon', caption: 'StarsIcon', dataField: 'starsIcon', presenter: startPresenter }, { id: 'size', caption: 'TV Display Size', dataField: 'size' }, { id: 'refreshRate', caption: 'RefreshRate', dataField: 'refreshRate' }, { id: 'resolution', caption: 'Television Resolution', dataField: 'resolution' }, { id: 'starsValue', caption: 'Avg. Customer Review', dataField: 'starsValue' }];
- DIVタグのグリッドIDを呼び出し、コードを初期化します。
var dataView = new GC.Spread.Views.DataView(document.getElementById('grid1'), data, columns, new GC.Spread.Views.Plugins.CardLayout({ cardHeight: 300, cardWidth: 210, rowTemplate: rowTemplate }));
フィルタリング条件を設定する関数を指定します。
var SlicerBase = (function() { var SlicerBase = function(container, slicerData, columnName) { var self = this; self.container = container; self.slicerData = slicerData; self.columnName = columnName; self.exclusiveDatas = slicerData.getExclusiveData(columnName); self.slicerData.attachListener(self); self.onDataLoaded(); } SlicerBase.prototype = { onDataLoaded: function() { var self = this; var container = self.container; var renderedHTML = self.getRenderedHTML(); container.append(renderedHTML); container.find('.slicer-item input[type=checkbox]').on('click', function(e) { e.preventDefault(); }); container.find('.slicer-item').on('mouseenter', function(e) { $(this).addClass("hover"); }).on('mouseleave', function(e) { $(this).removeClass("hover"); }).on('mousedown', { slicer: this }, function(e) { var slicer = e.data.slicer; var slicerItem = $(e.currentTarget); var targetInput = slicerItem.find('input[type=checkbox]'); if (targetInput) { targetInput.prop('checked', !targetInput.prop('checked')); } var condition = slicer.getFilterCondition(e, slicer); if (!condition) { slicer.slicerData.doUnfilter(slicer.columnName); } else { slicer.slicerData.doFilter(slicer.columnName, condition); } }); }, onFiltered: function(args) { clearSlicerItemClass.call(this); this.updateSlicerItem(); var newData = _.map(args.rowIndexes, function(index) { return data[index]; }); dataView.data.setSource_(newData); //Need to be replaced dataView.invalidate(); } }; function clearSlicerItemClass() { var items = this.container.find('.slicer-item'); var classes = ['filtered', 'filteredOutByOther']; _.each(items, function(item) { _.each(classes, function(itemClass) { $(item).removeClass(itemClass); }); }); } return SlicerBase; })(); var ResolutionFilter = (function(super_) { var ResolutionFilter = function(container, slicerData, columnName) { super_.call(this, container, slicerData, columnName); } extends_(ResolutionFilter, super_); ResolutionFilter.prototype.getRenderedHTML = function() { var self = this; var columnName = self.columnName; var datas = self.exclusiveDatas; var slicerData = self.slicerData; var header = '<div class="filter-details">' + getCaption(columnName) + '</div>'; var body = ''; for (var i = 0, length = datas.length; i < length; i++) { var count = slicerData.getRowIndexes(columnName, i).length; body += '<div class="slicer-item"><input type="checkbox" /><span>' + datas[i] + '</span><span style="color:#A29999">(' + count + ')</span></div>'; } return header + body; }; ResolutionFilter.prototype.getFilterCondition = function(e, slicer) { var container = slicer.container; var inputs = container.find('input[type=checkbox]'); var indexes = []; _.each(inputs, function(item, i) { if (item.checked) { indexes.push(i); } }); return indexes.length ? { exclusiveRowIndexes: indexes } : null; }; ResolutionFilter.prototype.updateSlicerItem = function() { var self = this; var items = this.container.find('.slicer-item'); var i; var length; var filteredItems = self.slicerData.getFilteredIndexes(self.columnName); for (i = 0, length = filteredItems.length; i < length; i++) { $(items[filteredItems[i]]).addClass("filtered"); } var filteredOutByOtherItems = self.slicerData.getFilteredOutIndexes(self.columnName, slicerComponentNS.FilteredOutDataType.byOtherColumns); for (i = 0, length = filteredOutByOtherItems.length; i < length; i++) { $(items[filteredOutByOtherItems[i]]).addClass("filteredOutByOther"); } }; return ResolutionFilter; })(SlicerBase); var DisplaySizeFilter = (function(super_) { var DisplaySizeFilter = function(container, slicerData, columnName) { this.rangeInfo = [{ range: { min: -Infinity, max: 32 }, label: '32 Inches & Under' }, { range: { min: 33, max: 43 }, label: '33 to 43 Inches' }, { range: { min: 44, max: 49 }, label: '44 to 49 Inches' }, { range: { min: 50, max: 59 }, label: '50 to 59 Inches' }, { range: { min: 60, max: 69 }, label: '60 to 69 Inches' }, { range: { min: 70, max: Infinity }, label: '70 Inches & Up' }]; super_.call(this, container, slicerData, columnName); }; extends_(DisplaySizeFilter, super_); DisplaySizeFilter.prototype.getRenderedHTML = function() { var self = this; var columnName = self.columnName; var slicerData = self.slicerData; var header = '<div class="filter-details">' + getCaption(columnName) + '</div>'; var body = ''; var info = self.rangeInfo; for (var i = 0, length = info.length; i < length; i++) { var count = slicerData.getData(columnName, info[i].range).length; body += '<div class="slicer-item"><input type="checkbox" /><span>' + info[i].label + '</span><span style="color:#A29999">(' + count + ')</span></div>'; } return header + body; }; DisplaySizeFilter.prototype.getFilterCondition = function(e, slicer) { var container = slicer.container; var inputs = container.find('input[type=checkbox]'); var ranges = []; _.each(inputs, function(item, i) { if (item.checked) { ranges.push(slicer.rangeInfo[i].range); } }); return ranges.length ? { ranges: ranges } : null; }; DisplaySizeFilter.prototype.updateSlicerItem = function() { var self = this; var i; var length; var j; var len; var filteredItems = self.slicerData.getFilteredIndexes(self.columnName); var filteredOutByOtherItems = self.slicerData.getFilteredOutIndexes(self.columnName, slicerComponentNS.FilteredOutDataType.byOtherColumns); var items = this.container.find('.slicer-item'); var slicerData = self.slicerData; var columnIndex = slicerData.getColumnIndex(self.columnName); for (j = 0, len = items.length; j < len; j++) { var range = self.rangeInfo[j].range; for (i = 0, length = filteredItems.length; i < length; i++) { if (range.min <= slicerData.data[filteredItems[i]][columnIndex] && slicerData.data[filteredItems[i]][columnIndex] <= range.max) { $(items[j]).addClass("filtered"); } } for (i = 0, length = filteredOutByOtherItems.length; i < length; i++) { if (range.min <= slicerData.data[filteredOutByOtherItems[i]][columnIndex] && slicerData.data[filteredOutByOtherItems[i]][columnIndex] <= range.max) { $(items[j]).addClass("filteredOutByOther"); } } } }; return DisplaySizeFilter; })(SlicerBase); var CustomerReviewFilter = (function(super_) { var CustomerReviewFilter = function(container, slicerData, columnName) { this.rangeInfo = [{ range: { min: 4, max: Infinity }, label: '<span class="stars-box stars-4"></span><span>&Up</span>' }, { range: { min: 3, max: Infinity }, label: '<span class="stars-box stars-3"></span><span>&Up</span>' }, { range: { min: 2, max: Infinity }, label: '<span class="stars-box stars-2"></span><span>&Up</span>' }, { range: { min: 1, max: Infinity }, label: '<span class="stars-box stars-1"></span><span>&Up</span>' }]; super_.call(this, container, slicerData, columnName); } extends_(CustomerReviewFilter, super_); CustomerReviewFilter.prototype.getRenderedHTML = function() { var self = this; var columnName = self.columnName; var slicerData = self.slicerData; var header = '<div class="filter-details">' + getCaption(columnName) + '</div>'; var body = ''; var info = self.rangeInfo; for (var i = 0, length = info.length; i < length; i++) { var count = slicerData.getData(columnName, info[i].range).length; body += '<div data-index=' + i + ' class="slicer-item">' + info[i].label + '<span style="color:#A29999">(' + count + ')</span></div>'; } return header + body; }; CustomerReviewFilter.prototype.getFilterCondition = function(e, slicer) { var currentTarget = e.currentTarget; var index = $(currentTarget).data('index'); return { ranges: [slicer.rangeInfo[index].range] }; }; CustomerReviewFilter.prototype.updateSlicerItem = function() { var self = this; var i; var length; var j; var len; var filteredItems = self.slicerData.getFilteredIndexes(self.columnName); var filteredOutByOtherItems = self.slicerData.getFilteredOutIndexes(self.columnName, slicerComponentNS.FilteredOutDataType.byOtherColumns); var items = this.container.find('.slicer-item'); var slicerData = self.slicerData; var columnIndex = slicerData.getColumnIndex(self.columnName); for (j = 0, len = items.length; j < len; j++) { var range = self.rangeInfo[j].range; for (i = 0, length = filteredItems.length; i < length; i++) { if (range.min <= slicerData.data[filteredItems[i]][columnIndex] && slicerData.data[filteredItems[i]][columnIndex] <= range.max) { $(items[j]).addClass("filtered"); } } for (i = 0, length = filteredOutByOtherItems.length; i < length; i++) { if (range.min <= slicerData.data[filteredOutByOtherItems[i]][columnIndex] && slicerData.data[filteredOutByOtherItems[i]][columnIndex] <= range.max) { $(items[j]).addClass("filteredOutByOther"); } } } }; return CustomerReviewFilter; })(SlicerBase); function getCaption(id) { var column = _.find(columns, function(col) { return col.id === id; }); return column.caption; } function extends_(d, b) { for (var p in b) { if (b.hasOwnProperty(p)) { d[p] = b[p]; } } function __() { this.constructor = d; } __.prototype = b.prototype; d.prototype = new __(); } var dataNames = _.keys(data[0]); var dataArr = []; _.forEach(data, function(item) { dataArr.push(_.values(item)); }); var dataSource = new slicerComponentNS.GeneralSlicerData(dataArr, dataNames); var TVDisplaySizeFilter = new DisplaySizeFilter($('#tv_display_size'), dataSource, 'size'); var TVResolutionFilter = new ResolutionFilter($('#tv_resolution'), dataSource, 'resolution'); var CustomerReviewFilter = new CustomerReviewFilter($("#customer_review_star"), dataSource, "starsValue");